home *** CD-ROM | disk | FTP | other *** search
/ Ham Radio 2000 #1 / Ham Radio 2000.iso / ham2000 / tcp_ip / wnos / wn941101 / pop3cli.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-10-08  |  9.8 KB  |  427 lines

  1. /* Post Office Protocol (POP3) Client -- RFC1460
  2.  * Copyright 1992 William Allen Simpson
  3.  * based on a POP2 Client by Mike Stockett, WA7DYX, et alia.
  4.  * Aug 94 ported to WNOS by DG1ZX
  5.  */
  6. #include <stdio.h>
  7. #include <fcntl.h>
  8. #include <time.h>
  9. #include <setjmp.h>
  10. #ifdef UNIX
  11. #include <sys/types.h>
  12. #endif
  13. #ifdef    __TURBOC__
  14. #include <dir.h>
  15. #include <io.h>
  16. #endif
  17. #include "global.h"
  18. #include "config.h"
  19.  
  20. #ifdef POP3_CLIENT
  21.  
  22. #ifdef    ANSIPROTO
  23. #include <stdarg.h>
  24. #endif
  25. #include "mbuf.h"
  26. #include "cmdparse.h"
  27. #include "proc.h"
  28. #include "socket.h"
  29. #include "timer.h"
  30. #include "netuser.h"
  31. #include "dirutil.h"
  32. #include "files.h"
  33. #include "smtp.h"
  34.  
  35.  
  36. /* mail separator */
  37. #define isNotSOM(x)        ((strncmp(x,"From ",5) != 0))
  38.  
  39. #define BUF_LEN    512
  40.  
  41. /* POP3 client control block */
  42. static struct pop3_ccb {
  43.     int  socket;        /* socket for this connection */
  44.     char state;        /* client state */
  45. #define    CALL    0
  46. #define PASS    1
  47. #define STAT    2
  48. #define XFER    3
  49. #define RETR    4
  50. #define DELE    5
  51. #define    ERROR    9
  52. #define    EXIT    10
  53.     char buf[BUF_LEN];    /* tcp input buffer pointer */
  54.     int  messages;        /* number of msgs in current folder */
  55.     int  msg_no;        /* current message number */
  56.     long bytes;        /* folder size */
  57. } *ccb;
  58. #define NULLCCB        (struct pop3_ccb *)0
  59.  
  60. static int16 Popquiet = 0;
  61. static int32 mailhost = 0;
  62.  
  63. static void pop_send __ARGS((int unused,void *cb1,void *p));
  64. static int poptick __ARGS((void));
  65. static int copymail __ARGS((char *spoolarea,char *filename,char *buf,unsigned len,FILE *wfp));
  66. static int recvmail __ARGS((struct pop3_ccb *cp,FILE *fp));
  67.  
  68.  
  69. /* Command string specifications */
  70. static char near mailbox_name[20],
  71.          username[20],
  72.          password[20],
  73.          Workfile_name[] = "mbox3.pop",
  74.          user_cmd[]      = "USER %s\n",
  75.          pass_cmd[]      = "PASS %s\n",
  76.          stat_cmd[]     = "STAT\n",
  77.          retr_cmd[]      = "RETR %d\n",
  78.          del_cmd[]     = "DELE %d\n",
  79.          quit_cmd[]      = "QUIT\n",
  80.          ok_rsp[]       = "+OK",    /* Response string keys */
  81.          err_rsp[]     = "-ERR";
  82.  
  83. #define OK_RSP_LEN 3
  84. #define ERR_RSP_LEN 4
  85.  
  86. static int
  87. domailbox(int argc,char *argv[],void *p) {
  88.   if(argc < 2 && mailbox_name[0] != '\0')
  89.     tprintf("%s\n",mailbox_name);
  90.   else
  91.     sprintf(mailbox_name,"%.18s",argv[1]);
  92.   return 0;
  93. }
  94.  
  95. static int
  96. domailhost(int argc,char *argv[],void *p) {
  97.   int32 n;
  98.  
  99.   if(argc < 2) {
  100.     tprintf("%s\n",inet_ntoa(mailhost));
  101.   }
  102.   else {
  103.     if((n = resolve(argv[1])) == 0) {
  104.       tprintf(Badhost,argv[1]);
  105.       return 1;
  106.     } else {
  107.       mailhost = n;
  108.     }
  109.   }
  110.   return 0;
  111. }
  112.  
  113. static int
  114. popkick(int argc,char *argv[],void *p) {
  115.   return (poptick());
  116. }
  117.  
  118. static int
  119. doquiet(int argc,char *argv[],void *p) {
  120.   return setintrc(&Popquiet,"POP3 quiet",argc,argv,0,3);
  121. }
  122.  
  123. static struct timer popcli_t;
  124.  
  125. static int
  126. dotimer(int argc,char *argv[],void *p) {
  127.   if(argc < 2) {
  128.     tprintf("POP3 timer %lu/%lu s\n",
  129.          read_timer(&popcli_t)/1000L,dur_timer(&popcli_t)/1000L);
  130.   }
  131.   else {
  132.     stop_timer(&popcli_t);
  133.     popcli_t.func = (void (*)())poptick;      /* what to call on timeout */
  134.     popcli_t.arg = NULLCHAR;            /* dummy value */
  135.     set_timer(&popcli_t,atol(argv[1])*1000L);    /* set timer duration */
  136.     start_timer(&popcli_t);            /* and fire it up */
  137.   }
  138.   return 0;
  139. }
  140.  
  141. static int
  142. douserdata(int argc,char *argv[],void *p) {
  143.   if (argc < 2 && username[0] != '\0')
  144.     tprintf("%s\n",username);
  145.   else if (argc != 3) {
  146.     tputs("Usage: pop3 userdata <username> <password>\n");
  147.     return 1;
  148.   }
  149.   else {
  150.     sprintf(username,"%.18s",argv[1]);
  151.     sprintf(password,"%.18s",argv[2]);
  152.   }
  153.   return 0;
  154. }
  155.  
  156. int
  157. dopop3(int argc,char *argv[],void *p) {
  158.   struct cmds Popcmds[] = {
  159.     "mailbox",    domailbox,    0,    0,    NULLCHAR,
  160.     "mailhost",    domailhost,    0,    0,    NULLCHAR,
  161.     "kick",    popkick,    0,    0,    NULLCHAR,
  162.     "quiet",    doquiet,    0,    0,    NULLCHAR,
  163.     "timer",    dotimer,    0,    0,    NULLCHAR,
  164.     "userdata",    douserdata,    0,    0,    NULLCHAR,
  165.     NULLCHAR,
  166.   };
  167.   return subcmd(Popcmds,argc,argv,p);
  168. }
  169.  
  170. static int
  171. poptick()
  172. {
  173.   int error = 0;
  174.  
  175.   if (ccb == NULLCCB) {
  176.     /* Don't start if any of the required parameters have not been specified */
  177.     if (mailhost == 0) {
  178.       tputs("POP3 Mailhost not set\n");
  179.       error = 1;
  180.     }
  181.     if (mailbox_name[0] == '\0') {
  182.       tputs("POP3 Mailbox not set\n");
  183.       error = 1;
  184.     }
  185.     if (username[0] == '\0' || password[0] == '\0') {
  186.       tputs("POP3 Username and/or password not set\n");
  187.       error = 1;
  188.     }
  189.     if(error == 0) {
  190.       if ((ccb = (struct pop3_ccb *)mxallocw(sizeof(struct pop3_ccb))) == NULLCCB) {
  191.     tputs("Unable to allocate CCB\n");
  192.     error = 1;
  193.       } else {
  194.     stop_timer(&popcli_t);
  195.     newproc("POP3 Client",1024,pop_send,0,ccb,NULL,0);
  196.       }
  197.     }
  198.   }
  199.   else {
  200.     start_timer(&popcli_t);
  201.   }
  202.   return error;
  203. }
  204.  
  205. static void
  206. pop_send(int unused,void *cb1,void *p) {
  207.   struct sockaddr_in fsocket;
  208.   FILE *mf;
  209.   char *cp;
  210.  
  211.   fsocket.sin_family = AF_INET;
  212.   fsocket.sin_addr.s_addr = mailhost;
  213.   fsocket.sin_port = IPPORT_POP3;
  214.  
  215.   ccb->socket = socket(AF_INET,SOCK_STREAM,0);
  216.   sockmode(ccb->socket,SOCK_ASCII);
  217.  
  218.   if (connect(ccb->socket,(char *)&fsocket,SOCKSIZE) != -1) {
  219.     log(ccb->socket,"POP3 connect");
  220.     ccb->state = CALL;
  221.     ccb->msg_no = 1;
  222.     ccb->messages = 0;
  223.  
  224.     for(;;) {
  225.  
  226.       if(ccb->state == ERROR)
  227.     break;
  228.  
  229.       if (ccb->state == EXIT) {
  230.     if (ccb->messages != 0) {
  231.       if (copymail(Mailspool,mailbox_name,&ccb->buf[0],BUF_LEN,mf) == 0) {
  232.         unlink(Workfile_name);
  233.         if (Popquiet < 2)
  234.           tprintf("New mail for %s from mailhost <%s> at %s%s",
  235.               mailbox_name, inet_ntoa(mailhost), ctime(&currtime),
  236.               (Popquiet < 1) ? "\007" : "");
  237.  
  238.         if (Popquiet == 2)
  239.           log(ccb->socket,"POP3  new mail <%s>",inet_ntoa(mailhost));
  240.       }
  241.       else
  242.        ccb->state = ERROR;
  243.     }
  244.     break;
  245.       }
  246.  
  247.       if ( recvline(ccb->socket,ccb->buf,BUF_LEN) == -1) {
  248.     ccb->state = ERROR;
  249.     break;
  250.       }
  251.       rip(ccb->buf);
  252.  
  253.       switch(ccb->state) {
  254.     case CALL:
  255.       if (strncmp(ccb->buf,ok_rsp,OK_RSP_LEN) == 0) {
  256.         usprintf(ccb->socket,user_cmd,username);
  257.         ccb->state = PASS;
  258.       } else {
  259.         ccb->state = ERROR;
  260.       }
  261.       break;
  262.  
  263.     case PASS:
  264.       if (strncmp(ccb->buf,ok_rsp,OK_RSP_LEN) == 0) {
  265.         usprintf(ccb->socket,pass_cmd,password);
  266.         ccb->state = STAT;
  267.       } else {
  268.         ccb->state = EXIT;
  269.       }
  270.       break;
  271.  
  272.     case STAT:
  273.       if (strncmp(ccb->buf,ok_rsp,OK_RSP_LEN) == 0) {
  274.         usputs(ccb->socket,stat_cmd);
  275.         ccb->state = XFER;
  276.       } else {
  277.         ccb->state = EXIT;
  278.       }
  279.       break;
  280.  
  281.     case XFER:
  282.       if (strncmp(ccb->buf,ok_rsp,OK_RSP_LEN) == 0) {
  283.         cp = ccb->buf;
  284.         while (*cp++ != ' ');
  285.         ccb->messages = atoi(cp);    /* get number of messages in the folder */
  286.         if (ccb->messages > 0) {
  287.           while (*cp++ != ' ');
  288.           ccb->bytes = atol(cp);    /* get folder size */
  289.           usprintf(ccb->socket,retr_cmd,ccb->msg_no);
  290.           ccb->state = RETR;
  291.           if ( (mf = open_file(Workfile_name,"a+",0,1)) == NULLFILE) {
  292.         ccb->state = ERROR;
  293.         break;
  294.           }
  295.           fseek(mf,0,SEEK_END);
  296.         }
  297.         else {
  298.           ccb->state = EXIT;
  299.         }
  300.       }
  301.       else {
  302.         ccb->state = EXIT;        /* EXIT because we have not send a DELE cmd */
  303.       }
  304.       break;
  305.  
  306.     case RETR:
  307.       if (strncmp(ccb->buf,ok_rsp,OK_RSP_LEN) == 0) {
  308.         if ( recvmail(ccb,mf) == -1 ) {
  309.           ccb->state = ERROR;
  310.         }
  311.         else {
  312.           usprintf(ccb->socket,del_cmd,ccb->msg_no);
  313.           ccb->state = DELE;
  314.         }
  315.       }
  316.       else {
  317.         if (strncmp(ccb->buf,err_rsp,ERR_RSP_LEN) == 0) {
  318.           if (++ccb->msg_no > ccb->messages)
  319.         ccb->state = EXIT;
  320.           else
  321.         usprintf(ccb->socket,retr_cmd,ccb->msg_no);
  322.          }
  323.          else {
  324.            ccb->state = ERROR;    /* don't send a QUIT cmd ==> UPDATE STATE */
  325.          }
  326.       }
  327.       break;
  328.  
  329.     case DELE:
  330.       if (++ccb->msg_no > ccb->messages)
  331.         ccb->state = EXIT;
  332.       else {
  333.         usprintf(ccb->socket,retr_cmd,ccb->msg_no);
  334.         ccb->state = RETR;
  335.       }
  336.       break;
  337.  
  338.     default:
  339.       break;
  340.       } /* end switch */
  341.     } /* end for(;;) */
  342.  
  343.     if (mf != NULLFILE)
  344.       fclose(mf);
  345.  
  346.     if (ccb->state == EXIT) {
  347.      usprintf(ccb->socket,quit_cmd);
  348.      recvline(ccb->socket,ccb->buf,BUF_LEN);
  349.     }
  350.     log(ccb->socket,"POP3 daemon exiting" );
  351.   }
  352.   else {
  353.     log(ccb->socket,"POP3 Connect failed");
  354.   }
  355.  
  356.   close_s(ccb->socket);
  357.   xfree((char *)ccb);
  358.   ccb = NULLCCB;
  359.   start_timer(&popcli_t);
  360.   return;
  361. }
  362.  
  363. /* Receive message from socket, copying to file.
  364.  * Returns number of lines received, -1 indicates error
  365.  */
  366. static int
  367. recvmail(struct pop3_ccb *cp,FILE *fp) {
  368.   int lines = 0;
  369.   int first_line = 1;
  370.  
  371.   while (recvline(cp->socket,cp->buf,BUF_LEN) != -1) {
  372.     char *p = cp->buf;
  373.  
  374.     if (first_line) {
  375.      if(isNotSOM(&cp->buf[0]))
  376.        fprintf(fp,"From POP3@%s at %s",inet_ntoa(mailhost),ptime(&currtime));
  377.      first_line = 0;
  378.     }
  379.  
  380.     /* check for end of message . or escaped .. */
  381.     if (*p == '.') {
  382.       if (*++p == '\n') {
  383.     return lines;
  384.       } else if ( *p != '.' ) {
  385.     p--;
  386.       }
  387.     }
  388.     /* Append to data file */
  389.     fputs(p,fp);
  390.     ++lines;
  391.   }
  392.   return -1;
  393. }
  394.  
  395. /* Copy from the work file into the mailbox.
  396.  * -1 indicates error
  397.  */
  398. static int
  399. copymail(char *spoolarea,char *filename,char *buf,unsigned len,FILE *wfp) {
  400.   FILE *mfp = NULLFILE;
  401.  
  402.   if (wfp == NULLFILE)
  403.     return -1;
  404.  
  405.   while (mlock(spoolarea,filename)) {
  406.     pause(10000L);            /* 10 seconds */
  407.   }
  408.  
  409.   sprintf(buf,"%s/%s.txt",spoolarea,filename);
  410.   if ((mfp = fopen(buf,APPEND_TEXT)) == NULL) {
  411.     tprintf("Can't open mailbox %s, new mail in %s\n", buf, Workfile_name);
  412.     rmlock(spoolarea,filename);
  413.     return -1;
  414.   }
  415.  
  416.   rewind(wfp);
  417.   while ( fgets(buf,len,wfp) != NULLCHAR ) {
  418.     fputs(buf,mfp);
  419.     pwait(NULL);    /* give other processes time in long copy */
  420.   }
  421.   fclose(mfp);
  422.   rmlock(spoolarea,filename);
  423.   return 0;
  424. }
  425.  
  426. #endif  /* POP3_CLIENT */
  427.